﻿using System;
using Microsoft.Practices.Unity;

namespace MvvmFx.Common.ViewModels.Factories
{
    /// <summary>
    /// Abstract base class for <see cref="IViewModelFactory{T}"/> objects.
    /// Creates an instance of a <see cref="IViewModelFactory{T}"/> class and populates 
    /// it with the data from the model.
    /// </summary>
    /// <remarks>
    /// An <see cref="ViewModelBase"/> abstracts and encapsulates the logic for
    /// creating and loading a <see cref="ICreateCriteria"/> object and hydrating it with
    /// data from the model.
    /// The overloaded Create method can take an <see cref="ViewModelBase"/> object
    /// to specify context-specific data about which model objects to load from.
    /// </remarks>
    public abstract class ViewModelFactory<T> : IViewModelFactory<T> where T : ViewModelBase
    {
        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="ViewModelFactory&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="container">The container.</param>
        protected ViewModelFactory(IUnityContainer container)
        {
            this.Container = container;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the Dependency container to be used for DI by derived classes.
        /// </summary>
        protected IUnityContainer Container
        {
            get;
            private set;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Creates and loads an existing <see cref="ViewModelBase"/> object of type T with data from the model.
        /// </summary>
        /// <returns>The <see cref="ViewModelBase"/> object of type T</returns>
        public T Create()
        {
            return OnCreate();
        }

        /// <summary>
        /// Creates and loads an existing <see cref="ViewModelBase"/> object of type T with data from the model.
        /// Uses the criteria parameter to determine which model data to load.
        /// </summary>
        /// <param name="criteria">The criteria to use to load the data from the model</param>
        /// <returns>The <see cref="ViewModelBase"/> object of type T</returns>
        public T Create(ICreateCriteria criteria)
        {
            return OnCreate(criteria);
        }

        #endregion

        #region Protected methods

        /// <summary>
        /// Override in derived classes to load an existing ViewModel object.
        /// </summary>
        /// <remarks>Do not call base.OnCreate()</remarks>
        protected virtual T OnCreate()
        {
            // This should never be called. If called by a consumer, it should be overridden.
            throw new NotImplementedException("ViewModelFactory.OnCreate() is not implemented");
        }

        /// <summary>
        /// Override in derived classes to load an existing ViewModel object.
        /// </summary>
        /// <remarks>Do not call base.OnCreate(criteria)</remarks>
        protected virtual T OnCreate(ICreateCriteria criteria)
        {
            // This should never be called. If called by a consumer, it should be overridden.
            throw new NotImplementedException("ViewModelFactory.OnCreate(criteria) is not implemented");
        }

        #endregion
    }
}